﻿using System;
using System.Linq;
using LuaNet;

namespace Luac
{

    /// <summary>
    /// $Id: luac.c,v 1.72 2015/01/06 03:09:13 lhf Exp $
    /// Lua compiler (saves bytecodes to files; also lists bytecodes)
    /// See Copyright Notice in lua.h
    /// </summary>
    class Program
    {
        const int EXIT_SUCCESS = 0;
        const int EXIT_FAILURE = -1;

        //#include "lobject.h"
        //#include "lstate.h"
        //#include "lundump.h"

        //static void PrintFunction(const Proto* f, int full);
        //#define luaU_print	PrintFunction

        const String PROGNAME = "luac";		/* default program name */
        const String OUTPUT = PROGNAME + ".out";	/* default output file */

        static int listing = 0;			/* list bytecodes? */
        static bool dumping = true;			/* dump bytecodes? */
        static bool stripping = false;			/* strip debug information? */
        static String Output = OUTPUT;	/* default output file name */
        static String output = Output;	/* actual output file name */
        static String progname = PROGNAME;	/* actual program name */

        static void exit(int code)
        {
#if DEBUG
            if (System.Diagnostics.Debugger.IsAttached)
            {
                Console.WriteLine("\nPress a key to quit...");
                Console.Read();
            }
#endif
            Environment.Exit(code);
        }

        static void fatal(String message)
        {
            Console.Error.WriteLine("{0}: {1}", progname, message);
            exit(EXIT_FAILURE);
        }

        //static void cannot(const char* what)
        //{
        //    fprintf(stderr, "%s: cannot %s %s: %s\n", progname, what, output, strerror(errno));
        //    exit(EXIT_FAILURE);
        //}

        static void usage(String message)
        {
            if (!String.IsNullOrWhiteSpace(message) && message.StartsWith("-"))
                Console.Error.WriteLine("{0}: unrecognized option '{1}'", progname, message);
            else
                Console.Error.WriteLine("{0}: {1}", progname, message);
            Console.Error.Write(
             "usage: {0} [options] [filenames]\n"
             + "Available options are:\n"
             + "  -l       list (use -l -l for full listing)\n"
             + "  -o name  output to file 'name' (default is \"{1}\")\n"
             + "  -p       parse only\n"
             + "  -s       strip debug information\n"
             + "  -v       show version information\n"
             + "  --       stop handling options\n"
             + "  -        stop handling options and process stdin\n"
             , progname, Output);
            exit(EXIT_FAILURE);
        }

        static int doargs(int argc, String[] argv)
        {
            int i;
            int version = 0;
            if (argv != null && argv.Length > 0 && !String.IsNullOrWhiteSpace(argv[0])) progname = argv[0];
            for (i = 1; i < argc; i++)
            {
                Func<String, bool> IS = s => String.Equals(argv[i], s);
                if (String.IsNullOrWhiteSpace(argv[i])) continue;
                if (!argv[i].StartsWith("-"))            /* end of options; keep it */
                    break;
                else if (IS("--"))          /* end of options; skip it */
                {
                    ++i;
                    if (version != 0) ++version;
                    break;
                }
                else if (IS("-"))           /* end of options; use stdin */
                    break;
                else if (IS("-l"))          /* list */
                    listing++;
                else if (IS("-o"))          /* output file */
                {
                    output = argv[++i];
                    if (String.IsNullOrWhiteSpace(output) || (output.StartsWith("-") && output.Length > 1))
                        usage("'-o' needs argument");
                    if (IS("-")) output = null;
                }
                else if (IS("-p"))          /* parse only */
                    dumping = false;
                else if (IS("-s"))          /* strip debug information */
                    stripping = true;
                else if (IS("-v"))          /* show version */
                    ++version;
                else                    /* unknown option */
                    usage(argv[i]);
            }
            if (i == argc && (listing != 0 || !dumping))
            {
                dumping = false;
                argv[--i] = Output;
            }
            if (version > 0)
            {
                using (var L = new LuaState())
                    Console.WriteLine(L.LuaCopyright);
                if (version == argc - 1) exit(EXIT_SUCCESS);
            }
            return i;
        }

        const String FUNCTION = "(function()end)();";

        //static const char* reader(lua_State* L, void* ud, size_t* size)
        //{
        //    UNUSED(L);
        //    if ((*(int*)ud)--)
        //    {
        //        *size = sizeof(FUNCTION) - 1;
        //        return FUNCTION;
        //    }
        //    else
        //    {
        //        *size = 0;
        //        return NULL;
        //    }
        //}

        //#define toproto(L,i) getproto(L->top+(i))

        //static const Proto* combine(lua_State* L, int n)
        //{
        //    if (n == 1)
        //        return toproto(L, -1);
        //    else
        //    {
        //        Proto* f;
        //        int i = n;
        //        if (lua_load(L, reader, &i, "=(" PROGNAME ")", NULL) != LUA_OK) fatal(lua_tostring(L, -1));
        //        f = toproto(L, -1);
        //        for (i = 0; i < n; i++)
        //        {
        //            f->p[i] = toproto(L, i - n - 1);
        //            if (f->p[i]->sizeupvalues > 0) f->p[i]->upvalues[0].instack = 0;
        //        }
        //        f->sizelineinfo = 0;
        //        return f;
        //    }
        //}

        //static int writer(lua_State* L, const void* p, size_t size, void* u)
        //{
        //    UNUSED(L);
        //    return (fwrite(p, size, 1, (FILE*)u) != 1) && (size != 0);
        //}

        static int pmain(LuaState L)
        {
            int argc = L.ToInteger(1);
            String[] argv = StateExtensions.ToUserData<String[]>(L, 2);
            //    const Proto* f;
            if (!L.CheckStack(argc)) fatal("too many input files");
            for (int i = 0; i < argc; i++)
            {
                Func<String, bool> IS = s => String.Equals(argv[i], s);
                string filename = IS("-") ? null : argv[i];
                if (L.LoadFile(filename) != LuaStatus.Ok) fatal(L.ToString(-1));
            }
            //    f = combine(L, argc);
            //    if (listing) luaU_print(f, listing > 1);
            //    if (dumping)
            //    {
            //        FILE* D = (output == NULL) ? stdout : fopen(output, "wb");
            //        if (D == NULL) cannot("open");
            //        lua_lock(L);
            //        luaU_dump(L, f, writer, D, stripping);
            //        lua_unlock(L);
            //        if (ferror(D)) cannot("write");
            //        if (fclose(D)) cannot("close");
            //    }
            return 0;
        }

        static int Main_luac(string[] args)
        {
            args = Environment.GetCommandLineArgs();
            int argc = args.Length;
            String[] argv = args;

            bool result = true;
            try
            {
                int i = doargs(argc, argv);
                argc -= i; argv = argv.Skip(i).ToArray(); //argv += i;
                if (argc <= 0) usage("no input files given");
                using (var L = new LuaState())
                {
                    L.PushFunction(pmain);
                    L.PushInteger(argc);
                    L.PushLightUserData(argv);
                    if (L.PCall(2, 0, 0) != LuaStatus.Ok) fatal(L.ToString(-1));
                }
            }
            catch (Exception ex)
            {
                fatal(String.Format("cannot create state: {0}", ex.GetBaseException().Message));
                result = false;
            }
#if DEBUG
            if (System.Diagnostics.Debugger.IsAttached)
            {
                Console.WriteLine("\nPress a key to quit...");
                Console.Read();
            }
#endif
            return result ? EXIT_SUCCESS : EXIT_FAILURE;
        }

    }
}


///*
//** $Id: print.c,v 1.76 2015/01/05 16:12:50 lhf Exp $
//** print bytecodes
//** See Copyright Notice in lua.h
//*/

//#include <ctype.h>
//#include <stdio.h>

//#define luac_c
//#define LUA_CORE

//#include "ldebug.h"
//#include "lobject.h"
//#include "lopcodes.h"

//#define VOID(p)		((const void*)(p))

//static void PrintString(const TString* ts)
//{
//    const char* s = getstr(ts);
//    size_t i, n = ts->len;
//    printf("%c", '"');
//    for (i = 0; i < n; i++)
//    {
//        int c = (int)(unsigned char)s[i];
//    switch (c)
//    {
//        case '"': printf("\\\""); break;
//        case '\\': printf("\\\\"); break;
//        case '\a': printf("\\a"); break;
//        case '\b': printf("\\b"); break;
//        case '\f': printf("\\f"); break;
//        case '\n': printf("\\n"); break;
//        case '\r': printf("\\r"); break;
//        case '\t': printf("\\t"); break;
//        case '\v': printf("\\v"); break;
//        default:
//            if (isprint(c))
//                printf("%c", c);
//            else
//                printf("\\%03d", c);
//    }
//}
// printf("%c",'"');
//}

//static void PrintConstant(const Proto* f, int i)
//{
//    const TValue* o = &f->k[i];
//    switch (ttype(o))
//    {
//        case LUA_TNIL:
//            printf("nil");
//            break;
//        case LUA_TBOOLEAN:
//            printf(bvalue(o) ? "true" : "false");
//            break;
//        case LUA_TNUMFLT:
//            {
//                char buff[100];
//                sprintf(buff, LUA_NUMBER_FMT, fltvalue(o));
//                printf("%s", buff);
//                if (buff[strspn(buff, "-0123456789")] == '\0') printf(".0");
//                break;
//            }
//        case LUA_TNUMINT:
//            printf(LUA_INTEGER_FMT, ivalue(o));
//            break;
//        case LUA_TSHRSTR:
//        case LUA_TLNGSTR:
//            PrintString(tsvalue(o));
//            break;
//        default:                /* cannot happen */
//            printf("? type=%d", ttype(o));
//            break;
//    }
//}

//#define UPVALNAME(x) ((f->upvalues[x].name) ? getstr(f->upvalues[x].name) : "-")
//#define MYK(x)		(-1-(x))

//static void PrintCode(const Proto* f)
//{
//    const Instruction* code = f->code;
//    int pc, n = f->sizecode;
//    for (pc = 0; pc < n; pc++)
//    {
//        Instruction i = code[pc];
//        OpCode o = GET_OPCODE(i);
//        int a = GETARG_A(i);
//        int b = GETARG_B(i);
//        int c = GETARG_C(i);
//        int ax = GETARG_Ax(i);
//        int bx = GETARG_Bx(i);
//        int sbx = GETARG_sBx(i);
//        int line = getfuncline(f, pc);
//        printf("\t%d\t", pc + 1);
//        if (line > 0) printf("[%d]\t", line); else printf("[-]\t");
//        printf("%-9s\t", luaP_opnames[o]);
//        switch (getOpMode(o))
//        {
//            case iABC:
//                printf("%d", a);
//                if (getBMode(o) != OpArgN) printf(" %d", ISK(b) ? (MYK(INDEXK(b))) : b);
//                if (getCMode(o) != OpArgN) printf(" %d", ISK(c) ? (MYK(INDEXK(c))) : c);
//                break;
//            case iABx:
//                printf("%d", a);
//                if (getBMode(o) == OpArgK) printf(" %d", MYK(bx));
//                if (getBMode(o) == OpArgU) printf(" %d", bx);
//                break;
//            case iAsBx:
//                printf("%d %d", a, sbx);
//                break;
//            case iAx:
//                printf("%d", MYK(ax));
//                break;
//        }
//        switch (o)
//        {
//            case OP_LOADK:
//                printf("\t; "); PrintConstant(f, bx);
//                break;
//            case OP_GETUPVAL:
//            case OP_SETUPVAL:
//                printf("\t; %s", UPVALNAME(b));
//                break;
//            case OP_GETTABUP:
//                printf("\t; %s", UPVALNAME(b));
//                if (ISK(c)) { printf(" "); PrintConstant(f, INDEXK(c)); }
//                break;
//            case OP_SETTABUP:
//                printf("\t; %s", UPVALNAME(a));
//                if (ISK(b)) { printf(" "); PrintConstant(f, INDEXK(b)); }
//                if (ISK(c)) { printf(" "); PrintConstant(f, INDEXK(c)); }
//                break;
//            case OP_GETTABLE:
//            case OP_SELF:
//                if (ISK(c)) { printf("\t; "); PrintConstant(f, INDEXK(c)); }
//                break;
//            case OP_SETTABLE:
//            case OP_ADD:
//            case OP_SUB:
//            case OP_MUL:
//            case OP_POW:
//            case OP_DIV:
//            case OP_IDIV:
//            case OP_BAND:
//            case OP_BOR:
//            case OP_BXOR:
//            case OP_SHL:
//            case OP_SHR:
//            case OP_EQ:
//            case OP_LT:
//            case OP_LE:
//                if (ISK(b) || ISK(c))
//                {
//                    printf("\t; ");
//                    if (ISK(b)) PrintConstant(f, INDEXK(b)); else printf("-");
//                    printf(" ");
//                    if (ISK(c)) PrintConstant(f, INDEXK(c)); else printf("-");
//                }
//                break;
//            case OP_JMP:
//            case OP_FORLOOP:
//            case OP_FORPREP:
//            case OP_TFORLOOP:
//                printf("\t; to %d", sbx + pc + 2);
//                break;
//            case OP_CLOSURE:
//                printf("\t; %p", VOID(f->p[bx]));
//                break;
//            case OP_SETLIST:
//                if (c == 0) printf("\t; %d", (int)code[++pc]); else printf("\t; %d", c);
//                break;
//            case OP_EXTRAARG:
//                printf("\t; "); PrintConstant(f, ax);
//                break;
//            default:
//                break;
//        }
//        printf("\n");
//    }
//}

//#define SS(x)	((x==1)?"":"s")
//#define S(x)	(int)(x),SS(x)

//static void PrintHeader(const Proto* f)
//{
//    const char* s = f->source ? getstr(f->source) : "=?";
//    if (*s == '@' || *s == '=')
//        s++;
//    else if (*s == LUA_SIGNATURE[0])
//        s = "(bstring)";
//    else
//        s = "(string)";
//    printf("\n%s <%s:%d,%d> (%d instruction%s at %p)\n",
//        (f->linedefined == 0) ? "main" : "function", s,
//       f->linedefined, f->lastlinedefined,
//       S(f->sizecode), VOID(f));
//    printf("%d%s param%s, %d slot%s, %d upvalue%s, ",
//       (int)(f->numparams), f->is_vararg ? "+" : "", SS(f->numparams),
//       S(f->maxstacksize), S(f->sizeupvalues));
//    printf("%d local%s, %d constant%s, %d function%s\n",
//       S(f->sizelocvars), S(f->sizek), S(f->sizep));
//}

//static void PrintDebug(const Proto* f)
//{
//    int i, n;
//    n = f->sizek;
//    printf("constants (%d) for %p:\n", n, VOID(f));
//    for (i = 0; i < n; i++)
//    {
//        printf("\t%d\t", i + 1);
//        PrintConstant(f, i);
//        printf("\n");
//    }
//    n = f->sizelocvars;
//    printf("locals (%d) for %p:\n", n, VOID(f));
//    for (i = 0; i < n; i++)
//    {
//        printf("\t%d\t%s\t%d\t%d\n",
//        i, getstr(f->locvars[i].varname), f->locvars[i].startpc + 1, f->locvars[i].endpc + 1);
//    }
//    n = f->sizeupvalues;
//    printf("upvalues (%d) for %p:\n", n, VOID(f));
//    for (i = 0; i < n; i++)
//    {
//        printf("\t%d\t%s\t%d\t%d\n",
//        i, UPVALNAME(i), f->upvalues[i].instack, f->upvalues[i].idx);
//    }
//}

//static void PrintFunction(const Proto* f, int full)
//{
//    int i, n = f->sizep;
//    PrintHeader(f);
//    PrintCode(f);
//    if (full) PrintDebug(f);
//    for (i = 0; i < n; i++) PrintFunction(f->p[i], full);
//}
